View Javadoc

1   // StreamManipulator.java, created Mon Jul  8  4:06:18 2002 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   
5   package joeq.ClassLib.Common.java.util.zip;
6   
7   /***
8    * StreamManipulator
9    *
10   * @author  John Whaley <jwhaley@alum.mit.edu>
11   * @version $Id: StreamManipulator.java 1451 2004-03-09 06:27:08Z jwhaley $
12   */
13  class StreamManipulator {
14      
15    private byte[] window;
16    private int window_start = 0;
17    private int window_end = 0;
18  
19    private int buffer = 0;
20    private int bits_in_buffer = 0;
21  
22    /***
23     * Get the next n bits but don't increase input pointer.  n must be
24     * less or equal 16 and if you if this call succeeds, you must drop
25     * at least n-8 bits in the next call.
26     * 
27     * @return the value of the bits, or -1 if not enough bits available.  */
28    public final int peekBits(int n)
29    {
30      if (bits_in_buffer < n)
31        {
32          if (window_start == window_end)
33            return -1;
34          buffer |= (window[window_start++] & 0xff
35                     | (window[window_start++] & 0xff) << 8) << bits_in_buffer;
36          bits_in_buffer += 16;
37        }
38      return buffer & ((1 << n) - 1);
39    }
40  
41    /* Drops the next n bits from the input.  You should have called peekBits
42     * with a bigger or equal n before, to make sure that enough bits are in
43     * the bit buffer.
44     */
45    public final void dropBits(int n)
46    {
47      buffer >>>= n;
48      bits_in_buffer -= n;
49    }
50  
51    /***
52     * Gets the next n bits and increases input pointer.  This is equivalent
53     * to peekBits followed by dropBits, except for correct error handling.
54     * @return the value of the bits, or -1 if not enough bits available. 
55     */
56    public final int getBits(int n)
57    {
58      int bits = peekBits(n);
59      if (bits >= 0)
60        dropBits(n);
61      return bits;
62    }
63    /***
64     * Gets the number of bits available in the bit buffer.  This must be
65     * only called when a previous peekBits() returned -1.
66     * @return the number of bits available.
67     */
68    public final int getAvailableBits()
69    {
70      return bits_in_buffer;
71    }
72  
73    /***
74     * Gets the number of bytes available.  
75     * @return the number of bytes available.
76     */
77    public final int getAvailableBytes()
78    {
79      return window_end - window_start + (bits_in_buffer >> 3);
80    }
81  
82    /***
83     * Skips to the next byte boundary.
84     */
85    public void skipToByteBoundary()
86    {
87      buffer >>= (bits_in_buffer & 7);
88      bits_in_buffer &= ~7;
89    }
90  
91    public final boolean needsInput() {
92      return window_start == window_end;
93    }
94  
95  
96    /* Copies length bytes from input buffer to output buffer starting
97     * at output[offset].  You have to make sure, that the buffer is
98     * byte aligned.  If not enough bytes are available, copies fewer
99     * bytes.
100    * @param length the length to copy, 0 is allowed.
101    * @return the number of bytes copied, 0 if no byte is available.  
102    */
103   public int copyBytes(byte[] output, int offset, int length)
104   {
105     if (length < 0)
106       throw new java.lang.IllegalArgumentException("length negative");
107     if ((bits_in_buffer & 7) != 0)  
108       /* bits_in_buffer may only be 0 or 8 */
109       throw new java.lang.IllegalStateException("Bit buffer is not aligned!");
110 
111     int count = 0;
112     while (bits_in_buffer > 0 && length > 0)
113       {
114         output[offset++] = (byte) buffer;
115         buffer >>>= 8;
116         bits_in_buffer -= 8;
117         length--;
118         count++;
119       }
120     if (length == 0)
121       return count;
122 
123     int avail = window_end - window_start;
124     if (length > avail)
125       length = avail;
126     java.lang.System.arraycopy(window, window_start, output, offset, length);
127     window_start += length;
128 
129     if (((window_start - window_end) & 1) != 0)
130       {
131         /* We always want an even number of bytes in input, see peekBits */
132         buffer = (window[window_start++] & 0xff);
133         bits_in_buffer = 8;
134       }
135     return count + length;
136   }
137 
138   public StreamManipulator()
139   {
140   }
141 
142   public void reset()
143   {
144     window_start = window_end = buffer = bits_in_buffer = 0;
145   }
146 
147   public void setInput(byte[] buf, int off, int len)
148   {
149     if (window_start < window_end)
150       throw new java.lang.IllegalStateException
151         ("Old input was not completely processed");
152 
153     int end = off + len;
154 
155     /* We want to throw an ArrayIndexOutOfBoundsException early.  The
156      * check is very tricky: it also handles integer wrap around.  
157      */
158     if (0 > off || off > end || end > buf.length)
159       throw new java.lang.ArrayIndexOutOfBoundsException();
160     
161     if ((len & 1) != 0)
162       {
163         /* We always want an even number of bytes in input, see peekBits */
164         buffer |= (buf[off++] & 0xff) << bits_in_buffer;
165         bits_in_buffer += 8;
166       }
167     
168     window = buf;
169     window_start = off;
170     window_end = end;
171   }
172 
173 }